//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using OpenLS.Core.Serialization;
using OpenLS.Drawing;
using OpenLS.Spreadsheet.Serialization;

namespace OpenLS.Spreadsheet.Drawing
{
    ///<summary>
    /// An anchor for a worksheet object. Uses two cells to anchor
    ///</summary>
    public class TwoCellAnchor : Anchor
    {
        public TwoCellAnchor(Worksheet ws)
            : base(ws)
        {
        }

        private CellAddress _cell;

        public CellAddress Cell
        {
            get { return _cell; }
            set
            {
                _cell = value;
                NotifyPropertyChanged("Cell");
            }
        }
        private CellAddress _secondCell;

        public CellAddress SecondCell
        {
            get { return _secondCell; }
            set { _secondCell = value;
                NotifyPropertyChanged("SecondCell");
            }
        }
	
	
        Point _fromOffset;
        Point _toOffset;
        public override double RowOffset
        {
            get { return _fromOffset.Y; }
            set
            {
                _fromOffset.Y = value;
                NotifyPropertyChanged("RowOffset");
            }
        }
        public override double ColumnOffset
        {
            get { return _fromOffset.X; }
            set
            {
                _fromOffset.X = value;
                NotifyPropertyChanged("ColumnOffset");
            }
        }
        #region IOfficeXmlSerializable Members

        internal override void ReadXmlCore(ReadContext context)
        {
            Contract.Ensures(Content != null);
            using (ReadContext c = context.Read(SpreadsheetConstants.SpreadsheetDrawingNamespace + "twoCellAnchor"))
            {
                editAs = c.GetOptionalString("editAs");
                c.PassAttributes();
                var from = c.ReadElementAs<Marker>("from");
                _fromOffset = new Point(from.ColumnOffset, from.RowOffset);
                var to = c.ReadElementAs<Marker>("to");
                _toOffset = new Point(to.ColumnOffset, to.RowOffset);
                Cell = new CellAddress(from.Row, from.Column);
                SecondCell = new CellAddress(to.Row, to.Column);

                Content = ReadContent(c);
                ClientData = c.ReadOptionalElement<AnchorClientData>(AnchorClientData.xmlName);
            }
        }

        internal string editAs;
        internal override void WriteXmlCore(WriteContext context)
        {
            using (WriteContext c = context.Write(SpreadsheetConstants.SpreadsheetDrawingNamespace + "twoCellAnchor"))
            {
                if (editAs != null)
                    c.SetString("editAs", editAs);
                {
                    var from = new Marker
                                   {
                                       Column = Cell.Column,
                                       Row = Cell.Row,
                                       ColumnOffset = _fromOffset.X,
                                       RowOffset = _fromOffset.Y
                                   };
                    c.WriteElementAs(from, "from");
                }
                {
                    var to = new Marker
                                 {
                                     Column = SecondCell.Column,
                                     Row = SecondCell.Row,
                                     ColumnOffset = _toOffset.X,
                                     RowOffset = _toOffset.Y
                                 };
                    c.WriteElementAs(to, "to");
                }
                Content.WriteXml(context);
                context.WriteElement(ClientData);
            }
        }

        #endregion

        public override double ActualWidth
        {
            get
            {
                double result = 0;
                Column lastColumn = Worksheet.Columns[SecondCell.Column];
                for (int i = Cell.Column; i < lastColumn.Index; i++)
                {
                    result += Worksheet.Columns[i].ActualWidth;
                }
                result -= _fromOffset.X;
                result += _toOffset.X;
                return result;

            }
            set
            {
                if (value < 0)
                    value = 0;
                double x = Worksheet.Columns[Cell.Column].Offset + ColumnOffset;
                int col = Cell.Column;
                while (Worksheet.Columns[col].Offset < x + value)
                    col++;
                col--;
                SecondCell = new CellAddress(SecondCell.Row, col);
                _toOffset = new Point(value - (Worksheet.Columns[SecondCell.Column].Offset - x), _toOffset.Y);
                NotifyPropertyChanged("ActualWidth");
                if (Content is ShapeBase)
                {
                    var b = (ShapeBase)Content;
                    b.Width = value;
                }
            }
        }

        //\\double actualHeight = 200;
        public override double ActualHeight
        {
            get {
                double result = 0;
                Row lastRow = Worksheet.Rows[SecondCell.Row];
                for (int i = Cell.Row; i < lastRow.Index; i++)
                {
                    result += Worksheet.Rows[i].ActualHeight;
                }
                result -= _fromOffset.Y;
                result += _toOffset.Y;
                return result;
            }
            set
            {
                if (value < 0)
                    value = 0;

                double y = Worksheet.Rows[Cell.Row].Offset + RowOffset;
                int row = Cell.Row;
                while (Worksheet.Rows[row].Offset < y + value)
                    row++;
                row--;
                SecondCell = new CellAddress(row, SecondCell.Column);
                _toOffset = new Point(_toOffset.X, value - (Worksheet.Rows[SecondCell.Row].Offset - y));
                if (Content is ShapeBase)
                {
                    var b = (ShapeBase)Content;
                    b.Height = value;
                }
                NotifyPropertyChanged("ActualHeight");
                
            }
        }

        internal override void DragSideImp(double amount, Dock sideType)
        {
            switch (sideType)
            {
                case Dock.Left:
                    if (Content is ShapeBase)
                    {
                        double angle = ((ShapeBase)Content).RotationAngle * Math.PI / 180;
                        ColumnOffset += Math.Cos(angle) * amount;
                        RowOffset += Math.Sin(angle) * amount;
                    }
                    else
                    {
                        ColumnOffset += amount;
                    }
                    NotifyPropertyChanged("ActualWidth");
                    if (Content is ShapeBase)
                    {
                        ((ShapeBase)Content).Width = ActualWidth;
                    }
                    //\\  this.ActualWidth += -amount; //\\remove
                    break;
                case Dock.Top:
                    if (Content is ShapeBase)
                    {
                        double angle = ((ShapeBase)Content).RotationAngle * Math.PI / 180;
                        RowOffset += Math.Cos(angle) * amount;
                        ColumnOffset -= Math.Sin(angle) * amount;
                    }
                    else
                        RowOffset += amount;
                    NotifyPropertyChanged("ActualHeight");
                    if (Content is ShapeBase)
                    {
                        ((ShapeBase)Content).Height = ActualHeight;
                    }
                    break;
                case Dock.Right:
                    ActualWidth += amount;
                    if (Content is ShapeBase)
                    {
                        ((ShapeBase)Content).Width = ActualWidth;
                    } break;
                case Dock.Bottom:
                    ActualHeight += amount;
                    if (Content is ShapeBase)
                    {
                        ((ShapeBase)Content).Height = ActualHeight;
                    }
                    break;
            }
        }


        internal override void DragImp(double x, double y)
        {
            //using (new NotifyPropertyChangedDelay(this))
            {
                ColumnOffset += x;
                RowOffset += y;
                _toOffset.X += x;
                _toOffset.Y += y;
                adjustAnchors();
            }
        }

        private  void adjustAnchors()
        {
            while (Cell.Row > 0 && RowOffset < 0)
            {
                RowOffset += Worksheet.Rows[Cell.Row].ActualHeight;
                Cell = new CellAddress(Cell.Row - 1, Cell.Column);
            }
            while (Cell.Column > 0 && ColumnOffset < 0)
            {
                if (Worksheet != null) ColumnOffset += Worksheet.Columns[Cell.Column].ActualWidth;
                Cell = new CellAddress(Cell.Row, Cell.Column - 1);
            }
            while (Cell.Row < Worksheet.MaxRowIndex - 1 && RowOffset > Worksheet.Rows[Cell.Row].ActualHeight)
            {
                RowOffset -= Worksheet.Rows[Cell.Row].ActualHeight;
                Cell = new CellAddress(Cell.Row + 1, Cell.Column);
            }
            while (Cell.Column < Worksheet.MaxColumnIndex - 1 &&
                   ColumnOffset > Worksheet.Columns[Cell.Column].ActualWidth)
            {
                ColumnOffset -= Worksheet.Columns[Cell.Row].ActualWidth;
                Cell = new CellAddress(Cell.Row, Cell.Column + 1);
            }
            Contract.Assert(_secondCell.Row >= 0);

            while (_secondCell.Row > 0 && _toOffset.Y < 0)
            {
                _toOffset.Y += Worksheet.Rows[_secondCell.Row].ActualHeight;
                _secondCell = new CellAddress(_secondCell.Row - 1, _secondCell.Column);
            }
            while (_secondCell.Column > 0 && _toOffset.X < 0)
            {
                _toOffset.X += Worksheet.Columns[_secondCell.Column].ActualWidth;
                _secondCell = new CellAddress(_secondCell.Row, _secondCell.Column - 1);
            }
            Contract.Assert(_secondCell.Row >= 0);
            while (_secondCell.Row < Worksheet.MaxRowIndex - 1 &&
                   _toOffset.Y > Worksheet.Rows[_secondCell.Row].ActualHeight)
            {
                _toOffset.Y -= Worksheet.Rows[_secondCell.Row].ActualHeight;
                _secondCell = new CellAddress(_secondCell.Row + 1, _secondCell.Column);
                Contract.Assert(_secondCell.Row >= 0);
            }
            while (_secondCell.Column < Worksheet.MaxColumnIndex - 1 &&
                   _toOffset.X > Worksheet.Columns[_secondCell.Column].ActualWidth)
            {
                _toOffset.X -= Worksheet.Columns[_secondCell.Row].ActualWidth;
                _secondCell = new CellAddress(_secondCell.Row, _secondCell.Column + 1);
            }
            Contract.Assert(_secondCell.Row >= 0);

        }

        internal void SetToOffset(Point point)
        {
            this._toOffset = point;
        }
    }
}